//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package fs_api_serv

import (
	"context"
	"strings"
	"time"

	"atomgit.com/openlinksaas-org/api-server/dao"
	"atomgit.com/openlinksaas-org/api-server/dao/bulletin_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/fs_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/idea_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/issue_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/project_entry_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/requirement_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/testcase_dao"
	"atomgit.com/openlinksaas-org/api-server/utils"
	"atomgit.com/openlinksaas-org/extension-proto-gen-go.git/extension_fs_api"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/fs_api"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_entry_api"
	"go.mongodb.org/mongo-driver/mongo"
)

func (apiImpl *FsApiImpl) GcProjectFs(ctx context.Context, req *fs_api.GcProjectFsRequest) (*fs_api.GcProjectFsResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}

	//检查权限
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	//只有管理员可以执行清理工作
	if !(projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin) {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	fsItem, err := fs_dao.FsInfoDao.Get(ctx, req.FsId)
	if err != nil {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_NO_FS,
			ErrMsg: "文件存储不存在",
		}, nil
	}
	if !(fsItem.OwnerType == int32(fs_api.FS_OWNER_TYPE_FS_OWNER_TYPE_PROJECT) && fsItem.OwnerId == req.ProjectId) {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_NO_FS,
			ErrMsg: "文件存储不存在",
		}, nil
	}

	if time.Now().Unix()-(fsItem.LastGcTime/1000) < 3600*12 {
		return &fs_api.GcProjectFsResponse{
			Code:   fs_api.GcProjectFsResponse_CODE_NO_PERMISSION,
			ErrMsg: "必须间隔12小时才能再次清理空间",
		}, nil
	}

	var gcFileItemList []*fs_dao.FileInfoDbItem

	if req.FsId == projectItem.ProjectFsId {
		gcFileItemList, err = apiImpl.gcProjectFs(ctx, req.ProjectId, req.FsId, projectItem.BasicInfo.ProjectDesc)
	} else if req.FsId == projectItem.IssueFsId {
		gcFileItemList, err = apiImpl.gcIssueFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.DocFsId {
		gcFileItemList, err = apiImpl.gcDocFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.RequireMentFsId {
		gcFileItemList, err = apiImpl.gcRequireMentFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.IdeaFsId {
		gcFileItemList, err = apiImpl.gcIdeaFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.BulletinFsId {
		gcFileItemList, err = apiImpl.gcBulletinFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.ApiCollFsId {
		gcFileItemList, err = apiImpl.gcApiCollFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.PagesFsId {
		gcFileItemList, err = apiImpl.gcPagesFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.TestCaseFsId {
		gcFileItemList, err = apiImpl.gcTestCaseFs(ctx, req.ProjectId, req.FsId)
	} else if req.FsId == projectItem.TestResultFsId {
		gcFileItemList, err = apiImpl.gcTestResultFs(ctx, req.ProjectId, req.FsId)
	}
	if err != nil {
		return nil, err
	}
	if len(gcFileItemList) == 0 {
		return &fs_api.GcProjectFsResponse{
			Code:        fs_api.GcProjectFsResponse_CODE_OK,
			GcFileCount: 0,
			GcTotalSize: 0,
		}, nil
	}
	//清理存储文件
	conn, err := utils.ConnGrpc(apiImpl.remoteAddr)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	client := extension_fs_api.NewExtensionFsApiClient(conn)
	for _, gcFileItem := range gcFileItemList {
		client.RemoveFile(ctx, &extension_fs_api.RemoveFileRequest{
			FsId:   gcFileItem.FsId,
			FileId: gcFileItem.FileId,
		}) //skip error check
		//XXX add log here
	}

	//清理数据库文件记录
	gcFileCount := uint32(0)
	gcTotalSize := uint64(0)
	for _, gcFileItem := range gcFileItemList {
		gcFileCount += 1
		gcTotalSize += uint64(gcFileItem.FileSize)
	}

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	_, err = dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		for _, gcFileItem := range gcFileItemList {
			err = fs_dao.FsFileDao.Remove(sessCtx, gcFileItem.FsId, gcFileItem.FileId)
			if err != nil {
				return nil, err
			}
		}
		err = fs_dao.FsInfoDao.IncStatus(ctx, req.FsId, int64(gcFileCount)*-1, int64(gcTotalSize)*-1)
		if err != nil {
			return nil, err
		}
		err = fs_dao.FsInfoDao.UpdateLastGcTime(ctx, req.FsId, time.Now().UnixNano()/1e6)
		return nil, err
	})

	if err != nil {
		return nil, err
	}

	return &fs_api.GcProjectFsResponse{
		Code:        fs_api.GcProjectFsResponse_CODE_OK,
		GcFileCount: gcFileCount,
		GcTotalSize: gcTotalSize,
	}, nil
}

func (apiImpl *FsApiImpl) gcProjectFs(ctx context.Context, projectId, fsId, projectDesc string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if strings.Contains(projectDesc, fileItem.FileId) {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcIssueFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}
	//列出所有issue
	issueIdList, err := issue_dao.IssueDao.DistinctIssueIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	recycleIssueIdList, err := issue_dao.IssueRecycleDao.DistinctIssueIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	issueIdList = append(issueIdList, recycleIssueIdList...)

	issueIdMap := map[string]bool{}
	for _, issueId := range issueIdList {
		issueIdMap[issueId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if issueIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcDocFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	docIdList, err := project_entry_dao.EntryDao.DistinctEntryIdByProject(ctx, projectId, project_entry_api.ENTRY_TYPE_ENTRY_TYPE_DOC)
	if err != nil {
		return nil, err
	}
	recycleDocIdList, err := project_entry_dao.EntryRecycleDao.DistinctEntryIdByProject(ctx, projectId, project_entry_api.ENTRY_TYPE_ENTRY_TYPE_DOC)
	if err != nil {
		return nil, err
	}
	docIdList = append(docIdList, recycleDocIdList...)

	docIdMap := map[string]bool{}
	for _, docId := range docIdList {
		docIdMap[docId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if docIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcRequireMentFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	reqIdList, err := requirement_dao.RequirementInfoDao.DistinctRequirementIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	recycleReqIdList, err := requirement_dao.RequirementRecycleDao.DistinctRequirementIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	reqIdList = append(reqIdList, recycleReqIdList...)

	reqIdMap := map[string]bool{}
	for _, reqId := range reqIdList {
		reqIdMap[reqId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if reqIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcIdeaFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	ideaIdList, err := idea_dao.IdeaDao.DistinctIdeaIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	recycleIdeaIdList, err := idea_dao.IdeaRecycleDao.DistinctIdeaIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	ideaIdList = append(ideaIdList, recycleIdeaIdList...)

	ideaIdMap := map[string]bool{}
	for _, ideaId := range ideaIdList {
		ideaIdMap[ideaId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if ideaIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcBulletinFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	bulletIdList, err := bulletin_dao.BulletinInfoDao.DistinctBulletIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	recycleBulletIdList, err := bulletin_dao.BulletinRecycleDao.DistinctBulletIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	bulletIdList = append(bulletIdList, recycleBulletIdList...)

	bulletIdMap := map[string]bool{}
	for _, bulletId := range bulletIdList {
		bulletIdMap[bulletId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if bulletIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcApiCollFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	apiCollIdList, err := project_entry_dao.EntryDao.DistinctEntryIdByProject(ctx, projectId, project_entry_api.ENTRY_TYPE_ENTRY_TYPE_API_COLL)
	if err != nil {
		return nil, err
	}

	recycleApiCollIdList, err := project_entry_dao.EntryRecycleDao.DistinctEntryIdByProject(ctx, projectId, project_entry_api.ENTRY_TYPE_ENTRY_TYPE_API_COLL)
	if err != nil {
		return nil, err
	}
	apiCollIdList = append(apiCollIdList, recycleApiCollIdList...)

	apiCollIdMap := map[string]bool{}
	for _, apiCollId := range apiCollIdList {
		apiCollIdMap[apiCollId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if apiCollIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcPagesFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	entryIdList, err := project_entry_dao.EntryDao.DistinctEntryIdByProject(ctx, projectId, project_entry_api.ENTRY_TYPE_ENTRY_TYPE_PAGES)
	if err != nil {
		return nil, err
	}
	recycleEntryIdList, err := project_entry_dao.EntryRecycleDao.DistinctEntryIdByProject(ctx, projectId, project_entry_api.ENTRY_TYPE_ENTRY_TYPE_PAGES)
	if err != nil {
		return nil, err
	}
	entryIdList = append(entryIdList, recycleEntryIdList...)

	entryIdMap := map[string]bool{}
	for _, entryId := range entryIdList {
		entryIdMap[entryId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if entryIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcTestCaseFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	caseIdList, err := testcase_dao.CaseInfoDao.DistinctIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	recycleCaseIdList, err := testcase_dao.CaseRecycleDao.DistinctIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	caseIdList = append(caseIdList, recycleCaseIdList...)

	caseIdMap := map[string]bool{}
	for _, caseId := range caseIdList {
		caseIdMap[caseId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if caseIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}

func (apiImpl *FsApiImpl) gcTestResultFs(ctx context.Context, projectId, fsId string) ([]*fs_dao.FileInfoDbItem, error) {
	//列出所有文件
	fileItemList, err := fs_dao.FsFileDao.ListByFsId(ctx, fsId, 7)
	if err != nil {
		return nil, err
	}

	testResultIdList, err := testcase_dao.ResultDao.DistinctIdByProject(ctx, projectId)
	if err != nil {
		return nil, err
	}
	testResultIdMap := map[string]bool{}
	for _, testResultId := range testResultIdList {
		testResultIdMap[testResultId] = true
	}

	gcList := []*fs_dao.FileInfoDbItem{}
	for _, fileItem := range fileItemList {
		if testResultIdMap[fileItem.OwnerId] {
			continue
		}
		gcList = append(gcList, fileItem)
	}
	return gcList, nil
}
